import UIKit

//扩展用于对已经存在的数据类型进行新功能的追加，而协议用于约定属性与方法供遵守它的数据类型实现。在Swift语言中，扩展与协议结合使用可以使代码结构更加规整，开发者在编写功能逻辑时更加方便简单，也更有利于程序的扩展性。
//熟悉Objective-C语言的读者知道，在Objective-C语言中有着类别的语法，其功能也是为已经存在的类添加新的功能。与Swift语言中的扩展不同的是，类别是有名称的，而扩展没有名称。Swift语言中的扩展支持如下功能：

//· 添加计算属性。
//· 定义示例方法和类型方法。
//· 定义新的构造方法。
//· 定义下标方法。
//· 定义嵌套类型。
//· 使一个已有的类型遵守协议。
//· 对协议进行扩展添加新的属性或方法约定。

//扩展使用extension关键字来进行定义

//创建一个类,有两个属性
class MyClass {
    var name:String
    var age:Int
    init() {
        name = "默认名"
        age = 0
    }
}

//为MyClass类扩展一个计算属性
extension MyClass {
    var nameAndAge:String {
        return "\(name) + \(age)"
    }
}

var xiaoming = MyClass()
//调用扩展中的新属性
print(xiaoming.nameAndAge)


//上面的代码演示了通过扩展为类新增计算属性。同样的，也可以为类型新增一个构造方法
extension MyClass {
    convenience init(name:String,age:Int){
        self.init()
        self.name = name
        self.age = age
    }
}

//使用扩展中的构造方法
var xiaoli = MyClass(name: "小李", age: 12)
print(xiaoli.nameAndAge)


//也可以通过扩展添加实例方法与类方法
extension MyClass {
    //扩展一个实例方法
    func logName() -> String{
        print(name)
        return name
    }
    
    //扩展一个类方法
    class func logClassName() {
        print("MyClass")
    }
}

var xiaowang = MyClass()
xiaowang.logName()
MyClass.logClassName()


//如果是对值类型进行扩展，可以使用mutating关键字来修饰方法，使得在方法内部可以直接修改当前实例本身，示例代码如下：

extension Int {
    //修改本身需要使用mutating
    mutating func change() {
        self = self * self
    }
}

var num1 = 3
num1.change()
print(num1)

//可以使用扩展来使某个类遵守一个协议
//定义一个协议
protocol MyProcotol {
    func myFunc()
}

//使用扩展是类型遵守协议
extension MyClass:MyProcotol {
    //必须对协议中的方法进行实现
    func myFunc() {
        print("myFunc")
    }
}

var cls = MyClass()
cls.myFunc()
//注意，如果使用扩展使某个数据类型遵守了一个协议，那么在此扩展中就需要实现协议中的方法。


//在Swift语言中，协议使用protocol关键字来创建，其中可以声明属性与方法。属性在具体实现时既可以是计算属性，也可以是存储属性。
protocol ProtocolOne{
    //定义实例属性
    //可读的
    var name:String{get}
    
    //可读可写
    var age:Int{set get}
    
    //可读的
    var nameAndAge:String{get}
    
    //定义静态属性
    static var className:String{get}
}
//创建一个类来遵守Protocol协议
class ClassOne:ProtocolOne {
    //进行协议中属性的实现
    var name: String
    var age: Int
    var nameAndAge: String{
        get{
            return "\(name) + \(age)"
        }
    }
    
    static var className: String {
        get{
            return "ClassOne"
        }
    }
    init() {
        name = "默认名"
        age = 0
    }
}


//在协议中进行方法的定义
protocol ProtocolTwo {
    //声明实例方法
    func logName()
    
    //声明静态方法
    static func LogClassName()
}

class ClassTwo:ProtocolTwo {
    var name:String
    var age:Int
    init() {
        name = "default"
        age = 0
    }
    func logName() {
        print(name)
    }
    static func LogClassName() {
        print("ClassTwo")
    }
}

//通过扩展也可以为协议中约定的属性方法提供默认的实现，这种语法功能的意义将十分重大。开发者可以为某个协议提供一个扩展，在扩展中为其约定的属性方法提供一套默认的实现，这样所有遵守此协议的数据类型都获取到了扩展中的默认实现
@objc protocol ClassProcotol:class {
    //此协议犯法可选实现
    @objc optional func log()
}

//为ClassProcotol中的方法提供默认实现
extension ClassProcotol {
    func log(){
        print("log")
    }
}

//遵守ClassProtocol协议
//如果这个类实现了协议中的可选方法,调用协议中的方法是就执行类内部的实现,
//如果这个类没有实现协议中的可选方法,调用协议中的方法时就会执行协议扩展中的默认实现
class ClassThree:ClassProcotol {
    func log() {
        print("ClassThree log")
    }
}
var classthree = ClassThree()
classthree.log()
